#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
#include <libgen.h>
#include <sys/types.h> 
#include <dirent.h>
#include <fcntl.h>
#define BLOCK 4096

int ftype(char *path){
	if(access(path,F_OK)==-1){
		//文件不存在
		return -1; 
	}else{
		//文件存在
		struct stat sbuf;
		if(lstat(path,&sbuf) < 0){
			perror("lstat ");
			exit(1);
		}
		if(S_ISREG(sbuf.st_mode)){
			return 1; 
		}else if(S_ISDIR(sbuf.st_mode)){
			return 2; 
		}else if(S_ISLNK(sbuf.st_mode)){
			return 3; 
		}
	}
	return 0; 
}

void toslink(int fd){
	//change reg file to soft link
}

int remove_dir(const char *dir)
{
    char cur_dir[] = ".";
    char up_dir[] = "..";
    char dir_name[128];
    DIR *pdir;
    struct dirent *dp;
    struct stat dir_stat;

    // 参数传递进来的目录不存在，直接返回
    if (access(dir, F_OK)==-1) {
        return 0;
    }

    // 获取目录属性失败，返回错误
    if (stat(dir, &dir_stat)<0) {
        perror("get directory stat error");
        return -1;
    }

    if (S_ISREG(dir_stat.st_mode) ) {  // 普通文件直接删除
        remove(dir);
    } else if (S_ISDIR(dir_stat.st_mode) ) {   // 目录文件，递归删除目录中内容
        pdir = opendir(dir);
        while ((dp=readdir(pdir)) != NULL ) {
            // 忽略 . 和 ..
            if ( (0 == strcmp(cur_dir, dp->d_name)) || (0 == strcmp(up_dir, dp->d_name)) ) {
                continue;
            }
            sprintf(dir_name, "%s/%s", dir, dp->d_name);
            remove_dir(dir_name);   // 递归调用
        }
        closedir(pdir);
        rmdir(dir);    // 删除空目录
    } else {
        perror("unknow file type!");
        exit(1);
    }
    return 1; 
}

void file2file(char *src,char *des){
	//打开src，只读 
	int fd1 = open(src,O_RDONLY);
	if(fd1<0){
		perror("open src file error");
		exit(1);
	}
	//打开des，如果存在，截断；如果不存在，创建 
	int fd2 = open(des,O_WRONLY|O_CREAT|O_TRUNC,0644);
	if(fd2<0){
		perror("open dest file error");
		exit(1);
	}
	//读src到buf，把buf写入des
	char buf[BLOCK];
	if(ftype(src)==3){		//复制软链接 
		int len = readlink(src,buf,sizeof(buf));
		write(fd2,buf,len);
		toslink(fd2);
	}else{					//复制普通文件 
		int n;
		while(n=read(fd1,buf,sizeof(buf))){
			write(fd2,buf,n);
		}
	}
	//关闭文件
	close(fd1);
	close(fd2);
	 
}
void file2dir(char *src,char *des){
	//改一下文件路径，在结尾加上src的文件名
	char newpath[256];
	sprintf(newpath,"%s/%s",des,basename(src));
	//转变为file2file 
	file2file(src,newpath); 
}

void dir2dir(char *src,char *des){
	//打开目录src
	DIR *dp1;
	dp1 = opendir(src);
	if(dp1==NULL){
		perror("open src dir error");
		exit(1);
	}
	//打开目录des
	if(ftype(des)==-1){
		//如果des不存在，创建 
		if(mkdir(des,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH)<0){
			perror("mkdir failed");
			exit(1);
		}
	}else if(ftype(des)==2){
		//如果存在des，删除该目录，重新创建 
		remove_dir(des);
		if(mkdir(des,S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH)<0){
			perror("mkdir failed");
			exit(1);
		}
	}
	struct dirent *srcdp;
	//遍历src目录项
	while((srcdp = readdir(dp1)) != NULL){
	    if(srcdp->d_name[0] == '.'){//跳过隐藏文件
			continue;
		}
		//拼接目录项路径 
		char newsrc[256];
		sprintf(newsrc,"%s/%s",src,srcdp->d_name);
		char newdes[256];
		sprintf(newdes,"%s/%s",des,srcdp->d_name);

		if(ftype(newsrc)==2){ //如果目录项是目录 
			dir2dir(newsrc,newdes); 
		}else{				//如果目录项是文件
			file2file(newsrc,newdes);
		}
	}
}
int main(int argc,char *argv[]){
	//获取命令行参数，
    //printf("argc = %d\n",argc);
	if(argc!=3){
		printf("命令格式错误\n");
		exit(1);	
	}
	
	char src[256],des[256];
	char cwd[128];
	getcwd(cwd,sizeof(cwd));
	sprintf(src,"%s/%s",cwd,argv[1]);
	sprintf(des,"%s/%s",cwd,argv[2]);
	
	//如果src = des ，不用复制，直接退出
	if(strcmp(src,des)==0){
		exit(1);
	} 
	//判断是哪一种,获取文件状态，
	if(ftype(src)==2){		//src is dir
		if(ftype(des)==2){
			//目标目录存在，src成为其子文件夹
			sprintf(des,"%s/%s",des,basename(src));
			dir2dir(src,des);
		}else if(ftype(des) == -1){
			//目标目录不存在，创建目录
			dir2dir(src,des);
		}else{
			printf("不能将目录复制到文件\n");
			exit(1);
		}
	}else if(ftype(src)==1 || ftype(src)==3){	//src is file
		if(ftype(des)==2){
			file2dir(src,des);
		}else{
			file2file(src,des);
		}
	}
	return 0;
}


